package com.nav.rummy.core;
import java.util.ArrayList;

public class Player {
	public enum Type {
		HUMAN, COMPUTER
	};

	private String name;
	private int score = 0, analyzedPts = 0, points = 0;
	private ArrayList<IArrangement> arrngmnt;
	
	private Deck cards = new Deck(14);

	public ArrayList<IArrangement> getArrangement() {
		return arrngmnt;
	}
	
	public Deck getCards() {
		return cards;
	}

	public Player(String name) {
		this.name = name;
	}

	public void addScore(int score) {
		this.score += score;
	}

	public String getName() {
		return name;
	}

	public int getScore() {
		return score;
	}

	public int getPoints() {
		return points;
	}

	public void setPoints(int points) {
		this.points = points;
	}

	public boolean addCard(Card card) {
		if(card == null)
			System.out.println("card is null");
		return cards.add(card);
	}

	public boolean removeCard(Card card) {
		return cards.remove(card);
	}

	private ArrayList<Sequence> getPureSeqs() {
		return getSequences(null);
	}

	private ArrayList<Sequence> getSequences(Card joker) {
		ArrayList<Sequence> seqs = new ArrayList<Sequence>();
		for (Card card : cards) {
			Sequence seq = new Sequence(joker);
			seq.add(card);
			seqs.add(seq);
		}
		for (int i = 0; i < seqs.size(); i++) {
			Sequence seq = seqs.get(i);
			for (Card card : cards) {
				if (!seq.contains(card) && seq.canAdd(card)) {
					Sequence newSeq = (Sequence) seq.clone();
					newSeq.add(card);
					seqs.add(newSeq);
				}
			}
			if (seq.size() < 3) {
				seqs.remove(seq);
				i--;
			}
		}
		return seqs;
	}

	private ArrayList<Set> getSets(Card joker) {
		ArrayList<Set> sets = new ArrayList<Set>();
		for (Card card : cards) {
			Set set = new Set(joker);
			set.add(card);
			sets.add(set);
		}
		for (int i = 0; i < sets.size(); i++) {
			Set set = sets.get(i);
			Card[] elts = new Card[set.size()];
			set.toArray(elts);
			int max = 0;
			for (Card c : elts) {
				int ind = cards.indexOf(c);
				if (ind > max)
					max = ind;
			}
			for (int j = max + 1; j < cards.size(); j++) {
				Card card = cards.get(j);
				if (set.canAdd(card)) {
					Set newSet = (Set) set.clone();
					newSet.add(card);
					sets.add(newSet);
				}
			}
			if (set.size() < 3) {
				sets.remove(set);
				i--;
			}
		}
		return sets;
	}

	private ArrayList<IArrangement> getArrangement(Card joker) {
		ArrayList<IArrangement> arrngmnt = new ArrayList<IArrangement>();
		Card outCard = null;
		analyzedPts = 0;
		for (Card c : cards) {
//			System.out.println("c: " + c + ", joker: " + joker);
			if(c == null)
				System.out.println("c is null");
			else if(joker == null)
				System.out.println("joker is null");
			else if (c.getRank() == joker.getRank())
				arrngmnt.add(c);
			else {
				if (outCard == null
						|| outCard.getRank().getPoints() < c.getRank()
								.getPoints()) {
					if (outCard != null) {
						analyzedPts += outCard.getRank().getPoints();
						arrngmnt.add(outCard);
					}
					outCard = c;
				} else {
					analyzedPts += c.getRank().getPoints();
					arrngmnt.add(c);
				}
			}
		}
		if(outCard != null)
			arrngmnt.add(outCard);
		return arrngmnt;
	}

	private ArrayList<IArrangement> analyzeMove3(Card joker) {
		ArrayList<Sequence> seqs = getSequences(joker);
		ArrayList<IArrangement> outArrngmnt = null;
		int minPts = 130;
		if (seqs != null)
			for (Sequence seq : seqs) {
				for (Card c : seq)
					cards.remove(c);
				ArrayList<IArrangement> arrngmnt = analyzeMove3(joker);
				if (analyzedPts < minPts) {
					minPts = analyzedPts;
					outArrngmnt = arrngmnt;
					outArrngmnt.add(0, seq);
				}
				for (Card c : seq) {
					if(c == null)
						System.out.println("c is null");
					cards.add(c);
				}
			}
		ArrayList<Set> sets = getSets(joker);
		if ((seqs == null || seqs.size() <= 0)
				&& (sets == null || sets.size() <= 0))
			return getArrangement(joker);
		for (Set set : sets) {
			for (Card c : set)
				cards.remove(c);
			ArrayList<IArrangement> arrngmnt = analyzeMove3(joker);
			if (analyzedPts < minPts) {
				minPts = analyzedPts;
				outArrngmnt = arrngmnt;
				outArrngmnt.add(0, set);
			}
			for (Card c : set) {
				if(c == null)
					System.out.println("c is null");
				cards.add(c);
			}
		}
		analyzedPts = minPts;
		return outArrngmnt;
	}

	private ArrayList<IArrangement> analyzeMove2(Card joker) {
		ArrayList<Sequence> seqs = getSequences(joker);
		ArrayList<IArrangement> outArrngmnt = null;
		if (seqs == null || seqs.size() <= 0)
			return getArrangement(joker);
		int minPts = 130;
		for (Sequence seq : seqs) {
			for (Card c : seq)
				cards.remove(c);
			ArrayList<IArrangement> arrngmnt = analyzeMove3(joker);
			if (analyzedPts < minPts) {
				minPts = analyzedPts;
				outArrngmnt = arrngmnt;
				outArrngmnt.add(0, seq);
			}
			for (Card c : seq) {
				if(c == null)
					System.out.println("c is null");
				cards.add(c);
			}
		}
		analyzedPts = minPts;
		return outArrngmnt;
	}

	private ArrayList<IArrangement> analyzeMove1(Card joker) {
		ArrayList<Sequence> purSeqs = getPureSeqs();
		ArrayList<IArrangement> outArrngmnt = null;
		if (purSeqs == null || purSeqs.size() <= 0)
			return getArrangement(joker);
		int minPts = 130;
		for (Sequence purSeq : purSeqs) {
			for (Card c : purSeq)
				cards.remove(c);
			ArrayList<IArrangement> arrngmnt = analyzeMove2(joker);
			if (analyzedPts < minPts) {
				minPts = analyzedPts;
				outArrngmnt = arrngmnt;
				outArrngmnt.add(0, purSeq);
			}
			for (Card c : purSeq) {
				if(c == null)
					System.out.println("c is null");
				cards.add(c);
			}
		}
		analyzedPts = minPts;
		return outArrngmnt;
	}

	private ArrayList<IArrangement> analyzeMove(Card inCard, Card joker) {
		if(inCard == null)
			System.out.println("inCard is null");
		cards.add(inCard);
		ArrayList<IArrangement> arrngmnt = analyzeMove1(joker);
		cards.remove(inCard);
		return arrngmnt;
	}

	public String getSuggestion(Deck closedDeck, Card openCard, Card joker) {
		ArrayList<IArrangement> openArrngmnt = analyzeMove(openCard, joker);
		Card openOutCard = (Card)openArrngmnt.get(openArrngmnt.size() - 1);
		int openPoints = analyzedPts;

		int sumClosedPoints = 0;
		for (Card card : closedDeck) {
			analyzeMove(card, joker);
			sumClosedPoints += analyzedPts;
		}
		double avgClosedPoints = (1.0 * sumClosedPoints)
				/ (1.0 * closedDeck.size());
		return openPoints <= avgClosedPoints ? (openPoints == 0 ? "O"
				+ openCard.getRank().toChar() + openCard.getSuit().toChar()
				+ " F" + openOutCard.getRank().toChar()
				+ openOutCard.getSuit().toChar() : "O"
				+ openCard.getRank().toChar() + openCard.getSuit().toChar()
				+ " O" + openOutCard.getRank().toChar()
				+ openOutCard.getSuit().toChar()) : "C";
	}

	public String getStrOutCardSuggestion(Deck nextPlayerCards, Card joker) {
		Card outCard = getOutCardSuggestion(nextPlayerCards, joker);
		return (analyzedPts > 0 ? "O" : "F") + outCard.getRank().toChar()
				+ outCard.getSuit().toChar();
	}

	public Card getOutCardSuggestion(Deck nextPlayerCards, Card joker) {
		arrngmnt = analyzeMove1(joker);
		System.out.println("Arrangement: " + arrngmnt);
		return (Card)arrngmnt.get(arrngmnt.size() - 1);
	}

	public int getOutCardSuggestionInd(Deck nextPlayerCards, Card joker) {
		return cards.indexOf(getOutCardSuggestion(nextPlayerCards, joker));
	}

	public String getInCardSuggestion(Deck closedDeck, Card openCard, Card joker) {
		analyzeMove(openCard, joker);
		int openPoints = analyzedPts;

		int sumClosedPoints = 0;
		for (Card card : closedDeck) {
			analyzeMove(card, joker);
			sumClosedPoints += analyzedPts;
		}
		double avgClosedPoints = (1.0 * sumClosedPoints)
				/ (1.0 * closedDeck.size());
		return openPoints <= avgClosedPoints ? "O"
				+ openCard.getRank().toChar() + openCard.getSuit().toChar()
				: "C";
	}

	public Card removeCard(Rank rank, Suit suit) {
		for (Card card : cards) {
			if(card == null)
				System.out.println("card is null");
			else if (card.getRank() == rank && card.getSuit() == suit) {
				cards.remove(card);
				return card;
			}
		}
		return null;
	}

	public boolean allCardsKnown() {
		return cards.size() >= 13;
	}

	public String toString() {
		return name + ": " + cards;
	}
}
